import { EStatusLoading } from ':/defined/status';
import { Draft, PayloadAction, createSlice } from '@reduxjs/toolkit';
import { TRootState } from '..';
import { Epic, ofType } from 'redux-observable';
import { mergeMap } from 'rxjs';

/**
 * 通用的 State 类型
 */
export interface ILoadingState<T = unknown> {
  // 加载状态
  loading: EStatusLoading
  // 数据
  data?: T
}

/**
 * 加载器参数
 */
export interface ILoaderParam<P> {
  payload?: P
  prevState: TRootState
}

/**
 * 创建带有 Loading 状态的 Slice
 * @param name 命名空间
 * @param loader 加载器
 * @param initialState 初始化状态
 * @returns slice
 */
export function createLoadingSlice<T, Name extends string, P = void>(
  name: Name,
  loader: (param: ILoaderParam<P>) => T | Promise<T>,
  initialState: ILoadingState<T> = { loading: EStatusLoading.IDLE },
) {

  const slice = createSlice({
    name,
    initialState,
    reducers: {
      idle(state) {
        state.loading = EStatusLoading.IDLE;
      },
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      load(state, _action: PayloadAction<P>) {
        state.loading = EStatusLoading.LOADING;
      },
      loaded(state, action: PayloadAction<Draft<T>>) {
        state.data = action.payload;
        state.loading = EStatusLoading.LOADED;
      },
      loadError(state) {
        state.loading = EStatusLoading.ERROR;
      },
      setData(state, action: PayloadAction<Draft<T>>) {
        state.data = action.payload;
      },
      setLoading(state, action: PayloadAction<Draft<EStatusLoading>>) {
        state.loading = action.payload;
      },
      setState(state, action: PayloadAction<Draft<ILoadingState<T>>>) {
        state.data = action.payload.data;
        state.loading = action.payload.loading;
      },
    },
  });

  return {
    ...slice,
    loadEpic: (($action, $state) => $action.pipe(
      ofType(slice.actions.load($action).type),
      mergeMap(async (action) => {
        try {
          // 去做异步请求
          const res = await loader({ payload: action.payload, prevState: $state.value });
          return slice.actions.loaded(res);
        } catch (err) {
          // 请求失败触发失败的动作
          return slice.actions.loadError();
        }
      }),
    )) as Epic<PayloadAction<P>, PayloadAction<P>, TRootState>,
  };
}